import simpleGit, { SimpleGit } from 'simple-git'
import { TEnvType } from './common'
import { select } from './sql'
import { Context } from 'koa'

class gitController {
  git: SimpleGit
  env: TEnvType

  constructor(pathname: string, env: TEnvType) {
    this.git = simpleGit({
      baseDir: pathname,
      binary: 'git',
      maxConcurrentProcesses: 6,
      trimmed: false
    })
    this.env = env
  }
  // 创建分支：
  // 1. gitlab不存在basic，报错；
  // 2. gitlab存在branch，报错；
  // 3. 服务器不存在branch，gitlab不存在，创建分支，推送远程
  // 4. 服务器存在branch，直接切，拉取；X
  // 5. 服务器不存在branch，gitlab存在，且basic是master，才能切；X
  async createBranch(ctx: Context) {
    const { branch, basic_branch, curBranch } = ctx.request.body as any
    try {
      await this.git.fetch(['--all'])
      const allBranches = await this.git.branch(['-a'])
      const branches = allBranches.all.filter((b) => !b.startsWith('remotes/origin/'))
      const remoteBranches = allBranches.all.filter((b) => b.startsWith('remotes/origin/'))
      console.warn('----- my data is remoteBranches: ', remoteBranches)

      if (!remoteBranches.includes(`remotes/origin/${basic_branch}`)) {
        throw new Error(`gitlab远程仓库 '${basic_branch}' 不存在，请确定basic_branch是否填写正确`)
      }
      if (remoteBranches.includes(`remotes/origin/${branch}`)) {
        throw new Error(`gitlab远程仓库存在 '${branch}'，可以选择切换分支`)
      }
      if (branches.includes(branch)) {
        throw new Error(`服务器已存在 '${branch}'，可以选择切换分支`)
      }
      if (branches.includes(basic_branch)) {
        await this.git.checkout(basic_branch)
      } else {
        await this.git.checkoutBranch(basic_branch, `remotes/origin/${basic_branch}`)
      }
      await this.git.pull('origin', basic_branch)
      await this.git.checkoutBranch(branch, basic_branch)
      await this.git.push('origin', branch, { '-u': null })
      /* abandon
      let msg = ''
      if (remoteBranches.includes(`remotes/origin/${branch}`)) {
        if (basic_branch !== 'master') {
          throw new Error(`gitlab远程仓库已存在${branch}分支，不支持从非master分支拉取`)
        }
        // 远程分支存在
        msg = `gitlab远程仓库存在${branch}分支，已将远程仓库代码同步到服务器`
        console.warn('----- my data is : 远程分支存在：', msg)
        await this.git.pull('origin', branch)
        return { type: 'add', msg }
      } else {
        // 远程分支不存在
        console.warn('----- my data is : 远程分支不存在：')
        await this.git.checkoutBranch(branch, basic_branch!)
        await this.git.push('origin', branch, { '-u': null })
        msg = `服务器创建分支成功，并成功推送到gitlab远程仓库`
        return { type: 'add', msg }
      } */
    } catch (error: any) {
      await this.git.checkout(curBranch)
      throw new Error(error.message)
    }
  }
  // 切换分支, 远程分支必存在
  async switchBranch(ctx: Context) {
    const { develop_branch_id, isPull, curBranch, canBack } = ctx.request.body as any
    try {
      let branch = ''
      if (['temp', 'master'].includes(develop_branch_id)) {
        branch = develop_branch_id
      } else {
        const [rows]: [any, any] = (await select('branches', { id: develop_branch_id })) as [any, any]
        if (!rows.length) {
          throw new Error('分支不在数据库中，可能是脏数据, 请关掉弹窗再尝试')
        }
        branch = rows[0].branch
      }
      await this.git.fetch(['--all'])
      const allBranches = await this.git.branch(['-a'])
      const remoteBranches = allBranches.all.filter((b) => b.startsWith('remotes/origin/'))
      const branches = allBranches.all.filter((b) => !b.startsWith('remotes/origin/'))
      if (!remoteBranches.includes(`remotes/origin/${branch}`)) {
        throw new Error('gitlab不存在此分支，请选择创建功能')
      }
      if (branches.includes(branch)) {
        // 服务器分支存在
        await this.git.checkout(branch)
        if (canBack) {
          // 当合并到temp/master的时候，发现服务器和远程的最新提交不一致，说明是temp/master有回滚，则初始化到第一条commit，再拉取
          const localCommitLogs = await this.git.log()
          this.git.fetch()
          const remoteCommitLogs = await this.git.log([`origin/${branch}`])
          if (remoteCommitLogs?.latest?.hash !== localCommitLogs?.latest?.hash) {
            if (localCommitLogs?.all.find((item) => item.hash === remoteCommitLogs?.latest?.hash)) {
              this.git.reset(['--hard', remoteCommitLogs?.latest?.hash!])
            } else {
              const firstCommitLog = localCommitLogs?.all?.[0]?.hash
              firstCommitLog && this.git.reset(['--hard', firstCommitLog])
            }
          }
        }
        isPull && (await this.git.pull('origin', branch))
        return { msg: `服务器分支存在，已切换${isPull ? '，已拉取最新代码' : '，并未拉取最新代码'}` }
      } else {
        // 服务器分支不存在
        await this.git.checkoutBranch(branch, `remotes/origin/${branch}`)
        return { msg: `已创建新分支，已和gitlab同步` }
      }
    } catch (error: any) {
      await this.git.checkout(curBranch)
      throw new Error(error.message)
    }
  }
  async getLocalCommitLogs() {
    return await this.git.log()
  }
  async getRemoteCommitLogs(branch: string) {
    await this.git.fetch()
    return await this.git.log([`origin/${branch}`])
  }
  async gitAddAll() {
    return await this.git.add('.')
  }
  async gitCommit(msg: string) {
    return await this.git.commit(msg)
  }
  async gitResetHard(targetCommit: string) {
    await this.git.reset(['--hard', targetCommit])
    await this.git.push(['--force'])
  }
  async gitMergeSameCommits(targetCommit: string, commitMsg: string) {
    await this.git.reset(['--soft', targetCommit])
    await this.gitAddAll()
    await this.gitCommit(commitMsg)
    await this.git.push(['--force'])
  }
  async gitPull() {
    return await this.git.pull()
  }
  async gitPush() {
    return await this.git.push()
  }
  // 将branch代码合并到当前分支
  async gitMerge(branch: string) {
    try {
      return await this.git.merge([branch])
    } catch (error: any) {
      await this.git.raw(['merge', '--abort'])
      throw new Error(error.message + '，已撤回')
    }
  }
  async cherryPick(commitHash: string) {
    try {
      return await this.git.raw(['cherry-pick', commitHash])
    } catch (error: any) {
      await this.git.raw(['cherry-pick', '--abort'])
      throw new Error(error.message + '，已撤回')
    }
  }
}

export const devGit: Recordable<gitController> = {}
export const testGit: gitController = new gitController(`${process.env.TEST_GIT_FOLDER}`, 'test')
export const onlineGit: gitController = new gitController(`${process.env.ONLINE_GIT_FOLDER}`, 'online')
;(async () => {
  const [rows]: [any, any] = ((await select('users', {})) || [{}]) as [any, any]
  rows.forEach((item: any) => {
    item.nickname &&
      (devGit[item.nickname] = new gitController(`${process.env.DEV_GIT_FOLDER}-${item.nickname}`, 'dev'))
  })
})()
